package com.example.bootdemo.service.impl;

import com.example.bootdemo.events.UserRegisterEvent;
import com.example.bootdemo.factory.SpecificationFactory;
import com.example.bootdemo.pojo.UserInfo;
import com.example.bootdemo.pojo.UserParam;
import com.example.bootdemo.repository.UserRepository;
import com.example.bootdemo.service.UserService;
import com.example.bootdemo.utils.EncryptionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private SimpleJpaRepository<UserInfo,Long> simpleJpaRepository;

    @Autowired
    ConfigurableApplicationContext applicationContext;

    @Transactional
    @Override
    public UserInfo save(UserInfo userInfo) {
        try {
            //数据入库前对密码进行加密
            String password = EncryptionUtil.md5Encrypt(userInfo.getPassword());
            userInfo.setPassword(password);
        } catch (Exception e) {
            log.error("encrypt password failed",e);
            return null;
        }
        UserInfo userInfo1 = userRepository.save(userInfo);
        //构造和发布用户注册事件
        UserRegisterEvent userRegisterEvent = new UserRegisterEvent(this,userInfo1);
        applicationContext.publishEvent(userRegisterEvent);
        return userInfo1;
    }

    @Transactional
    @Override
    public List<UserInfo> saveAll(List<UserInfo> users) {
        try{
            for(UserInfo userInfo: users){
                String password = EncryptionUtil.md5Encrypt(userInfo.getPassword());
                userInfo.setPassword(password);
            }
        }catch (Exception e){
            log.error("encrypt password failed",e);
            return null;
        }
        return userRepository.saveAll(users);
    }

    @Override
    public UserInfo queryById(Long userId) {
        return userRepository.findById(userId).get();
    }

    @Override
    public UserInfo queryByUserName(String userName) {
        return userRepository.findUserInfoByUserName(userName);
    }

    @Override
    public UserInfo findByTelNum(Long telNum) {
        return userRepository.findUserInfoByTelNumEquals(telNum);
    }

    @Override
    public UserInfo findByEmail(String email) {
        return userRepository.findUserInfoByEmail(email);
    }

    @Override
    public List<UserInfo> queryByLikeUserName(String userName) {
        return userRepository.findUserInfosByUserNameLike(userName);
    }

    @Override
    public List<UserInfo> queryLikeByUserNameCn(String userNameCn) {
        return userRepository.findUserInfosByUserNameCnLike(userNameCn);
    }

    @Override
    public List<UserInfo> findAllOrderBy(String propName, String order) {
        Sort.Direction direction;
        if("DESC".equalsIgnoreCase(order)){
            direction = Sort.Direction.DESC;
        }else{
            direction = Sort.Direction.ASC;
        }
        Sort sort = new Sort(direction,propName);
        return userRepository.findAll(sort);
    }

    @Override
    public Page<UserInfo> findAllByPage(int page, int pageSize) {

        PageRequest pageRequest = PageRequest.of(page,pageSize);

        return userRepository.findAll(pageRequest);
    }

    @Override
    public List<UserInfo> findByUserRole(String userRole) {

        return userRepository.findByUserRole(userRole);
    }

    @Override
    public List<UserInfo> findByLikeUserName(String userName) {

        return userRepository.findByLikeUserName(userName);
    }

    @Override
    public UserInfo queryByEmail(String email) {

        return userRepository.findByEmail(email);
    }

    @Override
    public List<UserInfo> findByUserNameOrderBy(String userName, String orderBy) {

        return userRepository.findByUserNameAndOrderBy(userName,orderBy);
    }

    @Override
    @Transactional
    public Integer  updateEmailByUsername(String username, String email) {

        return userRepository.updateEmailByUserName(username,email);

    }

    @Override
    public UserInfo findOneByExample(UserInfo userInfo) {
        ExampleMatcher exampleMatcher = ExampleMatcher.matchingAll();

        exampleMatcher.withMatcher("userName", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.EXACT,true));

        Example<UserInfo>   example = Example.of(userInfo,exampleMatcher);

        return userRepository.findOne(example).get();
    }

    @Override
    public List<UserInfo> findAllByExample(UserInfo userInfo) {

        ExampleMatcher exampleMatcher = ExampleMatcher.matchingAll();

        exampleMatcher.withMatcher("userRole", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.STARTING,true));

        exampleMatcher.withMatcher("userName",ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.ENDING,true));

        Example<UserInfo> example = Example.of(userInfo,exampleMatcher);

        return userRepository.findAll(example);
    }

    @Override
    public List<UserInfo> findAllByDynamicConditions(UserParam userParam) {

        return simpleJpaRepository.findAll((root,query,cb)->{
            List<Predicate> predicates = new ArrayList<>();
            if(!StringUtils.isEmpty(userParam.getUserName())){
                predicates.add(cb.like(root.get("userName"),userParam.getUserName()+"%"));
            }

            if(!StringUtils.isEmpty(userParam.getUserNameCn())){
                predicates.add(cb.like(root.get("userNameCn"),userParam.getUserNameCn()+"%"));
            }

            if(!StringUtils.isEmpty(userParam.getUserSex())){
                predicates.add(cb.equal(root.get("userSex"),userParam.getUserSex()));
            }

            if(userParam.getTelNum()!=null){
                predicates.add(cb.equal(root.get("telNum"),userParam.getTelNum()));
            }

            if(!StringUtils.isEmpty(userParam.getEmail())){
                predicates.add(cb.like(root.get("email"),userParam.getEmail()+"%"));
            }
            //根据时间区间查询
            if(userParam.getBeginCreateTime()!=null && userParam.getEndCreateTime()!=null){
                predicates.add(cb.between(root.get("createdTime"),userParam.getBeginCreateTime(),userParam.getEndCreateTime()));
            }

            return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();

        });
    }

    @Override
    public List<UserInfo> findAllByContainsLike(String attribute, String value) {

        return simpleJpaRepository.findAll(SpecificationFactory.containsLike(attribute,value));
    }

    @Override
    public List<UserInfo> findAllByContainsLikeAndBetween(String attribute, String value, Date min, Date max) {
        return simpleJpaRepository.findAll(SpecificationFactory
                .containsLike(attribute,value)
        .and(SpecificationFactory.isBetween("createdTime",min,max)));
    }
}
